/**
 *  system.cpp
 *  AwkwardCubed - Sys
 *
 *  Date: 20/08/2013
 *
 *  Language: C++
 *
 *  @author Carl mcCaffrey
 *
 */
#include "sys/system.h"

#include <intrin.h>
#include <sstream>

#include "sys/debug.h"

bool System::Init(HINSTANCE* hInstance, Timer* gameTimer)
{
	Debug& dbg = Debug::GetInstance();
	dbg.Init("debug.txt");
	dbg.Print("Initializing...\n");

	timer = gameTimer;

	RENDERER_DESC desc;
	desc.hInstance = hInstance;
	desc.wndSize = XMFLOAT2(0, 0);
	desc.wndFullscreen = false;
	desc.wndCursorVisible = true;
	desc.d3dClearColor = XMFLOAT4(0.0f, 0.0f, 0.25f, 1.0f);
	desc.d3dVSync = true;
	desc.d3dMSAASamples = 4;

	renderer = new Renderer;
	renderer->Init(desc);

	// Print System Description
	SystemInfo sysInfo = GetSystemInfo();
	dbg.Print("\nSystem Description:\n");
	dbg.Print("\tOS: %s\n", sysInfo.os.c_str());
	dbg.Print("\tCPU: %s\n", sysInfo.cpu.c_str());
	dbg.Print("\tTotal RAM: %iMB\n", sysInfo.totalRAM);
	dbg.Print("\tFree RAM: %iMB\n", sysInfo.availRAM);
	dbg.Print("\tRAM Used: %iMB (%i%%)\n", sysInfo.usedRAM, sysInfo.usedRAMPercent);
	dbg.Print("\tVRAM: %iMB\n", renderer->vRAM);
	dbg.Print("\tVideo Card: %s\n", renderer->vCard);

	dbg.Print("\nInitialization Complete!\n------------------------\n\n");

	return true;
}

void System::Shutdown(void)
{
	Debug& dbg = Debug::GetInstance();
	dbg.Print("\n------------------------\nShutting Down...\n");
	dbg.Shutdown();

	SafeDelete(renderer);

	exit(0);
}

void System::Update(void)
{
	timer->Tick();
#ifdef _DEBUG
	CalculateFrameStats();
#endif // _DEBUG

	renderer->ClearRenderTarget();

	renderer->Present();
}

void System::CalculateFrameStats(void)
{
	static int frameCount = 0;
	static float timeElapsed = 0.0f;

	frameCount++;

	if((timer->TotalTime() - timeElapsed) >= 1.0f){
		float fps = static_cast<float>(frameCount);
		float msPerFrame = 1000.0f / fps;

		std::stringstream string;
		string.precision(6);
		string << WINDOW_TITLE << "    "
			   << "FPS: " << fps << "    "
			   << "Frame Time: " << msPerFrame << "(ms)";
		SetWindowText(renderer->WndHandle(), string.str().c_str());

		frameCount = 0;
		timeElapsed += 1.0f;
	}
}

typedef BOOL (WINAPI * PFNGETPRODUCTINFOPROC)(DWORD, DWORD, DWORD, DWORD, PDWORD); // To Get Windows Version

SystemInfo System::GetSystemInfo(void)
{
	// Get OS Info
	std::string os;
	OSVERSIONINFOEX osVersionInfo;

	osVersionInfo.dwOSVersionInfoSize = sizeof(OSVERSIONINFOEX);
	GetVersionEx((OSVERSIONINFO*)&osVersionInfo);
	if((osVersionInfo.dwMajorVersion == 6) && (osVersionInfo.dwMinorVersion == 0)){
		os = "Windows Vista";
	}else if((osVersionInfo.dwMajorVersion == 6) && (osVersionInfo.dwMinorVersion == 1)){
		os = "Windows 7";
	}else if((osVersionInfo.dwMajorVersion == 6) && (osVersionInfo.dwMinorVersion == 2)){
		os = "Windows 8";
	}

	ulong osType;
	PFNGETPRODUCTINFOPROC GetProductInfo = (PFNGETPRODUCTINFOPROC)GetProcAddress(GetModuleHandle("kernel32.dll"), "GetProductInfo");
	GetProductInfo(osVersionInfo.dwMajorVersion, osVersionInfo.dwMinorVersion, 0, 0, &osType);
	switch(osType){
		case PRODUCT_ULTIMATE:
			os += " Ultimate Edition";
			break;

		case PRODUCT_PROFESSIONAL:
			os += " Professional Edition";
			break;

		case PRODUCT_HOME_PREMIUM:
			os += " Home Premium Edition";
			break;

		case PRODUCT_HOME_BASIC:
			os += " Home Basic Edition";
			break;

		case PRODUCT_STARTER:
			os += " Starter Edition";
			break;

		case PRODUCT_ENTERPRISE:
			os += " Enterprise Edition";
			break;
	}

	// Get CPU Info
	char cpuChar[0x40];
	int cpuInfo[4] = { 
		-1
	};

	__cpuid(cpuInfo, 0x80000000);
	unsigned exIDs = cpuInfo[0];
	memset(cpuChar, 0, sizeof(cpuChar));
	for(int i = 0x80000000; i <= exIDs; i++){
		__cpuid(cpuInfo, i);
		if(i == 0x80000002){
			memcpy(cpuChar, cpuInfo, sizeof(cpuInfo));
		}else if(i == 0x80000003){
			memcpy(cpuChar + 16, cpuInfo, sizeof(cpuInfo));
		}else if(i == 0x80000004){
			memcpy(cpuChar + 32, cpuInfo, sizeof(cpuInfo));
		}
	}
	std::string string(cpuChar), cpuString;
	for(int i = 0; i < string.size(); i++){
		if(string[i] != ' '){
			for(int x = i; x < string.size(); x++){
				cpuString += string[x];
			}
			break;
		}
	}


	// Get RAM Stats
	MEMORYSTATUSEX memStats;
	memStats.dwLength = sizeof(MEMORYSTATUSEX);
	GlobalMemoryStatusEx(&memStats);

	SystemInfo sysInfo;
	sysInfo.os = os;
	sysInfo.cpu = cpuString;
	sysInfo.totalRAM = static_cast<int>((memStats.ullTotalPhys / 1024) / 1024);
	sysInfo.availRAM = static_cast<int>((memStats.ullAvailPhys / 1024) / 1024);
	sysInfo.usedRAM = static_cast<int>((memStats.ullTotalPhys / 1024) / 1024) - static_cast<int>((memStats.ullAvailPhys / 1024) / 1024);
	sysInfo.usedRAMPercent = static_cast<int>(memStats.dwMemoryLoad);

	return sysInfo;
}